/*
* BinaryMatrixRank.cpp
* Last edited: 21.07.2014
* Author: David Noack
*/

#include "BinaryMatrixRank.h"


BinaryMatrixRank::BinaryMatrixRank(bool* bitArray, int bitArraySize) : NistTest(bitArray, bitArraySize)
{
	matrixRows = 32;
	matrixColumns = matrixRows;
}



double BinaryMatrixRank::execute(int size)
{
	n = size;
	double numerator = 0.0;
	double denominator = 0.0;

	//step 1: calcutelate the number of matrices and create a empty matrix
	numberOfmatrices = (n / (matrixRows*matrixColumns));

	//create matrix
	matrix = new bool*[matrixColumns];
	for (int j = 0; j < matrixColumns; j++)
		matrix[j] = new bool[matrixRows];


	//for every matrix do
	for (int i = 0; i < numberOfmatrices; i++)
	{
		//fill matrix with input data
		fillMatrix(i);

		//determine rank: step 2
		//printMatrix();
		performForwardOperations();
		performBackwardOperations();
		//increase counter of the determined rank: step 3
		determineRank();

	}

	//step 4: compute chi square
	//compute the first part with the value of full ranked matrices
	numerator = (countFullRank - 0.2888 * numberOfmatrices);
	chi_sqr = (numerator * numerator) / (0.2888 * numberOfmatrices);

	//compute the second part with the value of full_ranked-1 matrices
	numerator = (countFullRank_minus1 - 0.5776 * numberOfmatrices);
	chi_sqr += (numerator * numerator) / (0.5776 * numberOfmatrices);

	//compute the third part with all the other ranked matrices
	numerator = (countOtherRanks - 0.1336 * numberOfmatrices);
	chi_sqr += (numerator * numerator) / (0.1336 *numberOfmatrices);

	//step 5: compute p_value
	p_value = boost::math::gamma_q(1, chi_sqr/2);

	return p_value;

}

void BinaryMatrixRank::fillMatrix(int matrixCount)
{
	int offset = 1024 * matrixCount;

	for (int i = 0; i < matrixRows; i++)
	{
		for (int j = 0; j < matrixColumns; j++)
		{
			matrix[j][i] = bitArray[j + (i*matrixColumns+offset)];
		}
	}

}


void BinaryMatrixRank::performForwardOperations()
{
	bool foundOne = false;
	int row = 0;
	int tempCol = 0; //col var for XoR operations

	for (int col = 0; col < matrixColumns; col++)
	{
		if (matrix[col][col] == false) //this element on the diagonal is 0
		{
			row = col+1;
			foundOne = false;
			while (!foundOne)
			{
				if (matrix[col][row] == true)
				{
					swap(col,row);
					foundOne = true;
				}
				else
				{
					if (row < matrixRows)
						row++;
					else
						foundOne = true; //all other rows got no ONE so leave the loop and do nothing
				}
			}
			//printMatrix();
		}
		if (matrix[col][col] == true) //this element on the diagonal is 1
		{
			row = col + 1; //a
			
			for (; row < matrixRows; row++)
			{
				if (matrix[col][row] == true)
				{
					tempCol = col; //b
					for (; tempCol < matrixColumns; tempCol++)//e+f
					{
							matrix[tempCol][row] = xor(matrix[tempCol][row], matrix[tempCol][col]);
					}
				}
			}
			//printMatrix();
		}

	}

}


void BinaryMatrixRank::performBackwardOperations()
{
	bool foundOne = false;
	int row = 0;
	int tempCol = 0; //col var for XoR operations

	for (int col = matrixColumns-1; col >= 0; col--)
	{
		if (matrix[col][col] == false) //this element on the diagonal is 0
		{
			row = col - 1;
			foundOne = false;
			while (!foundOne)
			{
				if (matrix[col][row] == true)
				{
					swap(col, row);
					foundOne = true;
				}
				else
				{
					if (row >= 0)
						row--;
					else
						foundOne = true; //all other rows got no ONE so leave the loop and do nothing
				}
			}
			//printMatrix();
		}
		if (matrix[col][col] == true) //this element on the diagonal is 1
		{
			row = col - 1; //a

			for (; row >= 0; row--)
			{
				if (matrix[col][row] == true)
				{
					tempCol = col; //b
					for (; tempCol >= 0; tempCol--)//e+f
					{
						matrix[tempCol][row] = xor(matrix[tempCol][row], matrix[tempCol][col]);
					}
				}
			}
			//printMatrix();
		}

	}
}


void BinaryMatrixRank::swap(short row1, short row2)
{
	bool tempVar = false;

	for (int i = 0; i < matrixColumns; i++)
	{
		tempVar = matrix[i][row1]; //save value from first row into tempVar
		matrix[i][row1] = matrix[i][row2]; //fill the value from row2 into row1
		matrix[i][row2] = tempVar;//fill the value from row 1 into row 2
	}

}

void BinaryMatrixRank::printMatrix()
{
	std::cout << "Aktuelle Matrix: \n";

	for (int row = 0; row < matrixRows; row++)
	{
		for (int col = 0; col < matrixColumns; col++)
		{
			if (matrix[col][row] == true)
				std::cout << "| 1 ";
			else
				std::cout << "| 0 ";
		}
		std::cout << "| \n";
	}
}

void BinaryMatrixRank::determineRank()
{
	int non_zero_count = 0;

	//Search the matrix for non-zero rows
	for (int row = 0; row < matrixRows; row++)
	{
		for (int col = 0; col < matrixColumns; col++)
		{
			if (matrix[col][row] == true)
			{
				non_zero_count++; //found a one in a row
				col = matrixColumns;
			}		
		}	
	}

	if (non_zero_count == matrixRows)
		countFullRank++;
	else
	{
		if (non_zero_count == matrixRows - 1)
			countFullRank_minus1++;
		else
			countOtherRanks++;
	}
}